package rtcf.framework
{
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.NetStatusEvent;
	import flash.events.TimerEvent;
	import flash.net.ObjectEncoding;
	import flash.utils.Timer;
	
	[Event(name="connectResult", type="flash.events.NetStatusEvent")]
	[Event(name="connectFault",  type="flash.events.NetStatusEvent")]

	public class RTCFConnectionFactory extends EventDispatcher{
		
		//// ---- ---- Public Properties ---- ---- ////
		public var quitOnFirstRejection:Boolean = false;
		public var connectionClient:Object = null;
		public var currentConnection:RTCFConnection = null;
		
		//// ---- ---- Private Properties ---- ---- ////
		private var pendingConnections:Array;
		private var configXML:XML;
	   	private var failedConnections:Number;
	   	private var maxPendingConnections:Number; 
	   	private var nextPendingConnection:Number; 
	   	private var connectionEvent:NetStatusEvent;
	   	private var timers:Array;
	   	private var credentials:Credentials;
	   	private var busy:Boolean;
	   				
		public function RTCFConnectionFactory(target:IEventDispatcher=null){
			super(target);
			configXML = null;
			pendingConnections = [];
			timers = [];
			busy = false;
		}
		
		public function get config():XML{
	   		return configXML;
	   	}
	   	
	   	public function set config(configXML:XML):void{
	   		this.configXML = configXML;
	   	}
	   	
	   	public function connect(credentials:Credentials):Boolean{
	   		if (busy) return false;
	   		busy = true;
	   		this.credentials = credentials;
			init();
	   		addPendingConnection(null);
	   		return true;
	   	}
	   	
	   	private function init():void{
	   		
	   		if(configXML == null){
	   			trace("ERROR: configXML not set on RTCFConnectionFactory.");
	   			return;
	   		}
	   		
	   		var i:int;
	   		var rtcfConnection:RTCFConnection; 
	   		var timer:Timer;   
	   		busy = false;
	   		
	   		// Close and delete any connection objects left in the 
	   		// pendingConnections array.
	   		for (i = 0; i < pendingConnections.length; i++){
	   			rtcfConnection = pendingConnections[i] as RTCFConnection;
	   			if (rtcfConnection) {
					clearConnection(rtcfConnection);
					pendingConnections[i] = null;			
	   			}
	   		}
	   		
	   		// Stop and delete any timers in the timers array:
	   		for (i = 0; i < timers.length; i++){
	   			timer = timers[i] as Timer;
	   			if (timer) {
		   			timer.reset();
		   			timers[i] = null;		
	   			}
	   		}   		
	   		
	   		// Truncate the arrays:
	   		pendingConnections.length = 0; 
	   		timers.length = 0;
	   		
	   		// Clear or reset other variables:
	   		nextPendingConnection = 0;
	   		failedConnections = 0;
	   		maxPendingConnections = configXML.connectionSequence.connect.length();
	   		
	   		// Clear the current connection:
	   		if (currentConnection){
	   			currentConnection = null;
	   		}
	   	}
	   	
	   	private function clearConnection(rtcfConnection:RTCFConnection):void{
	   		rtcfConnection.close();
			if (connectionClient) rtcfConnection.client = {};
			rtcfConnection.removeEventListener("connectResult", connectResult);
			rtcfConnection.removeEventListener("connectFault", connectFault);  			
	   	}
	   	
	   	private function addPendingConnection(event:TimerEvent):void{
	   		
	   		if(configXML == null){
	   			trace("ERROR: configXML not set on RTCFConnectionFactory.");
	   			return;
	   		}	
	   		
	   		// Collect the connection information and create a uri string to connect to.
	   		
	   		// Start with default values
	   		var protocol:String = "rtmp";
	   		var host:String = "localhost";
	   		var applicationName:String = "";
	   		var instanceName:String = ""; 
	   		var port:String = "";
	   		var waitTime:int = 300;
	   		var objectEncoding:int = ObjectEncoding.DEFAULT;
	   		
	   		// Where attributes exist in the RTCFApplication tag overwrite the defaults:
	   		if (configXML.@protocol.toString.length > 0) protocol = configXML.@protocol;
	   		if (configXML.@host.toString().length > 0) host = configXML.@host;
	   		if (configXML.@name.toString().length > 0) applicationName = configXML.@name;
	   		if (configXML.@instance.toString().length > 0) instanceName = configXML.@instance;  	   		
			if (configXML.@port.toString().length > 0) port = configXML.@port;
	   		if (configXML.@waitTime.toString().length > 0) waitTime = parseInt(configXML.@waitTime);
			if (configXML.@objectEncoding.toString().length > 0) objectEncoding = ObjectEncoding[configXML.@objectEncoding];   		
	   		
	   		// Where attributes exist in the Connect tag overwrite the values:
	   		var connectXML:XML = configXML.connectionSequence.connection[nextPendingConnection];
	   		if (connectXML.@protocol.toString.length > 0) protocol = connectXML.@protocol;
	   		if (connectXML.@host.toString().length > 0) host = connectXML.@host;
	   		if (connectXML.@name.toString().length > 0) applicationName = connectXML.@name;
	   		if (connectXML.@instance.toString().length > 0) instanceName = connectXML.@instance;  	   		
			if (connectXML.@port.toString().length > 0) port = connectXML.@port;
	   		if (connectXML.@waitTime.toString().length > 0) waitTime = parseInt(connectXML.@waitTime);
			if (connectXML.@objectEncoding.toString().length > 0) objectEncoding = ObjectEncoding[connectXML.@objectEncoding];  
	   		
	   		// Compose the connection URI for this connection attempt:
	   		var uri:String = protocol + "://" + host;
	   		
	   		// If the port is an empty string don't include it:
	   		if(port){
	   			uri += ":" + port;
	   		}
	   		uri += "/" + applicationName;
	   		
	   		// If the instance name is an empty string do not append it:
	   		if(instanceName){
	   			uri += "/" + instanceName;
	   		}

	   		// Create an RTCFConnection, client object, and listeners:
	   		var rtcfConnection:RTCFConnection = new RTCFConnection();
	   		if (connectionClient) rtcfConnection.client = connectionClient;
			rtcfConnection.addEventListener("connectResult", connectResult);
			rtcfConnection.addEventListener("connectFault", connectFault);
			rtcfConnection.connect(uri, credentials);
	   		pendingConnections.push(rtcfConnection);
	   		nextPendingConnection++;
	   		
	   		if (nextPendingConnection < maxPendingConnections){
	   			var delay:Number = parseInt( configXML.connectionSequence.connect[nextPendingConnection].@delay);
					var timer:Timer = new Timer(delay, 1);
					timer.addEventListener(TimerEvent.TIMER, addPendingConnection);
					timer.start();
					timers.push( timer );
	   		}
	   	}
	   	
	   	private function connectResult(event:NetStatusEvent):void{
			var rtcfConnection:RTCFConnection = event.target as RTCFConnection;
			rtcfConnection.removeEventListener("connectResult", connectResult);
			rtcfConnection.removeEventListener("connectFault", connectFault);  
	
	   		// Remove the reference to the connection in the pendingConnections array
	   		// before clearing the array.
	   		for (var i:int = 0; i < pendingConnections.length; i++){
	   			if (rtcfConnection == pendingConnections[i]){
	   				pendingConnections[i] = null;
	   				break;
	   			}
	   		}
	   		
	   		init();
	   		currentConnection = rtcfConnection;
	   		dispatchEvent(event);
	   	}
	   	
	   	private function connectFault(event:NetStatusEvent):void{
	   		var rtcfConnection:RTCFConnection = event.target as RTCFConnection;
				 		
	   		// Find the fmsConnection in the pending connections array then clear and remove it:
	   		for (var i:int = 0; i < pendingConnections.length; i++){
	   			if (rtcfConnection == pendingConnections[i]) {
	   				clearConnection(rtcfConnection);
	   				pendingConnections[i] = null;
	   				break;
	   			}
	   		} 
	   		
	   		if (quitOnFirstRejection && event.info.code == "NetConnection.Connect.Reject" ){
	   			failedConnections = maxPendingConnections;
	   		}
	   		
	   		failedConnections++;
	   		
	   		if (failedConnections >= maxPendingConnections){
	     		init();
	   			dispatchEvent( event );
	   		}
	   	} 	   			   		   		   	
		
	}// Class
}// Package